package com.uduemc.biso.node.web.api.controller.noauthentication.service;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.github.pagehelper.PageInfo;
import com.uduemc.biso.core.extities.center.Host;
import com.uduemc.biso.core.extities.center.custom.NodeDomainInfos;
import com.uduemc.biso.core.extities.center.custom.nodedomaininfos.NodeDomain;
import com.uduemc.biso.core.extities.center.custom.nodedomaininfos.NodeDomainNgxinServer;
import com.uduemc.biso.core.utils.DomainUtil;
import com.uduemc.biso.core.utils.RestResult;
import com.uduemc.biso.node.core.entities.HDomain;
import com.uduemc.biso.node.core.utils.RestResultUtil;
import com.uduemc.biso.node.web.api.service.AsyncDomainService;
import com.uduemc.biso.node.web.api.service.DomainService;
import com.uduemc.biso.node.web.api.service.HostService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;

@RequestMapping("/api/service/master/domain")
@Controller
@Slf4j
public class ASDomainInfoController {

    @Resource
    private DomainService domainServiceImpl;

    @Resource
    private AsyncDomainService asyncDomainServiceImpl;

    @Resource
    private HostService hostServiceImpl;

    @Resource
    private HttpServletRequest request;

    /**
     * 获取域名信息
     *
     * @param domainName
     * @param hostId
     * @param domainType
     * @param status
     * @param orderBy
     * @param page
     * @param pageSize
     * @return
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws JsonProcessingException
     * @throws IOException
     */
    @PostMapping("/infos")
    @ResponseBody
    public RestResult infos(
            // 模糊查询域名
            @RequestParam(value = "domainName", required = false, defaultValue = "") String domainName,
            // 过滤 hostId 的部分
            @RequestParam(value = "hostId", required = false, defaultValue = "-1") long hostId,
            // 过滤 domainType 的部分
            @RequestParam(value = "domainType", required = false, defaultValue = "-1") short domainType,
            // 过滤 status 域名的状态部分
            @RequestParam(value = "status", required = false, defaultValue = "-1") short status,
            // 排序方式 1-顺序 2-倒序
            @RequestParam(value = "orderBy", required = false, defaultValue = "1") int orderBy,
            // 分页当前页
            @RequestParam(value = "page", required = false, defaultValue = "1") int page,
            // 分页的步长
            @RequestParam(value = "pageSize", required = false, defaultValue = "12") int pageSize)
            throws IOException {

        PageInfo<HDomain> findPageInfo = domainServiceImpl.findPageInfo(domainName, hostId, domainType, status, orderBy, page, pageSize);

        NodeDomainInfos result = new NodeDomainInfos();
        result.setTotal(findPageInfo.getTotal());

        List<HDomain> list = findPageInfo.getList();
        List<NodeDomain> rows = new ArrayList<NodeDomain>(list.size());
        if (!CollectionUtils.isEmpty(list)) {
            for (HDomain hDomain : list) {
                NodeDomain item = new NodeDomain();
                BeanUtils.copyProperties(hDomain, item);
                rows.add(item);
            }
        }
        result.setRows(rows);

        return RestResult.ok(result);
    }

    /**
     * 获取域名相关的全部信息
     *
     * @param domainName
     * @param hostId
     * @param domainType
     * @param status
     * @param orderBy
     * @param page
     * @param pageSize
     * @return
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws JsonProcessingException
     * @throws IOException
     */
    @PostMapping("/infos-all")
    @ResponseBody
    public RestResult infosAndNginxServer(
            // 模糊查询域名
            @RequestParam(value = "domainName", required = false, defaultValue = "") String domainName,
            // 过滤 hostId 的部分
            @RequestParam(value = "hostId", required = false, defaultValue = "-1") long hostId,
            // 过滤 domainType 的部分
            @RequestParam(value = "domainType", required = false, defaultValue = "-1") short domainType,
            // 过滤 status 域名的状态部分
            @RequestParam(value = "status", required = false, defaultValue = "-1") short status,
            // 排序方式 1-顺序 2-倒序
            @RequestParam(value = "orderBy", required = false, defaultValue = "1") int orderBy,
            // 分页当前页
            @RequestParam(value = "page", required = false, defaultValue = "1") int page,
            // 分页的步长
            @RequestParam(value = "pageSize", required = false, defaultValue = "12") int pageSize)
            throws IOException {

        RestResult restResult = this.infos(domainName, hostId, domainType, status, orderBy, page, pageSize);
        if (restResult.getCode() != 200 || restResult.getData() == null) {
            return restResult;
        }

        NodeDomainInfos data = RestResultUtil.data(restResult, NodeDomainInfos.class);
        if (data.getTotal() == 0) {
            return restResult;
        }

        List<NodeDomain> rows = data.getRows();
        String asciiDomain = null;
        for (NodeDomain nodeDomain : rows) {
            NodeDomainNgxinServer nodeDomainNgxinServer = new NodeDomainNgxinServer();
            nodeDomainNgxinServer.setDomain(nodeDomain.getDomainName());
            asciiDomain = DomainUtil.toASCII(nodeDomain.getDomainName());
            if (StrUtil.isBlank(asciiDomain)) {
                return RestResult.error(nodeDomain.getDomainName() + " 非有效域名。");
            }
            nodeDomainNgxinServer.setNgxinServer(domainServiceImpl.ngxinServerContent(nodeDomain.getHostId(), asciiDomain));
            nodeDomainNgxinServer.setFileLastModified(domainServiceImpl.nginxServerFileLastModified(nodeDomain.getHostId(), asciiDomain));
            nodeDomain.setNgxinServer(nodeDomainNgxinServer);
        }

        return RestResult.ok(data);
    }

    /**
     * 通过参数 domainName 以及 hostId 分别查看域名对应的数据库 icp 信息以及 缓存当中的 icp 信息
     * <p>
     * 只对用户绑定的域名生效，不会对系统域名生效
     *
     * @param domainName
     * @param hostId
     * @return
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws JsonProcessingException
     * @throws IOException
     */
    @PostMapping("/icp-infos")
    @ResponseBody
    public RestResult icpinfos(
            // 重置缓存的域名信息
            @RequestParam(value = "domainName", required = false, defaultValue = "") String domainName,
            // 重置缓存的 hostId 信息
            @RequestParam(value = "hostId", required = false, defaultValue = "-1") long hostId)
            throws IOException {
        if (StrUtil.isBlank(domainName) && hostId < 1) {
            Map<String, Object> message = new HashMap<>();
            message.put("domainName,hostId", "domainName 参数为空同时参数 hostId 小于1！");
            return RestResult.error(message);
        }

        if (StrUtil.isNotBlank(domainName)) {
            HDomain hDomain = domainServiceImpl.getInfoByDomainName(domainName);
            if (hDomain == null) {
                Map<String, Object> message = new HashMap<>();
                message.put("domainName", "未能通过 " + domainName + " 找到对应的 HDomain 数据信息！");
                return RestResult.error(message);
            }

            Map<String, Object> icpInfos = domainServiceImpl.icpInfos(hDomain);
            List<Map<String, Object>> result = new ArrayList<>();
            result.add(icpInfos);
            return RestResult.ok(result);
        }

        if (hostId > 0) {
            Host host = hostServiceImpl.getInfoById(hostId);
            if (host == null) {
                Map<String, Object> message = new HashMap<>();
                message.put("hostId", "未能通过 " + hostId + " 找到对应的 Host 数据信息！");
                return RestResult.error(message);
            }
            List<HDomain> bindDomain = domainServiceImpl.bindIndos(hostId);
            if (CollectionUtils.isEmpty(bindDomain)) {
                Map<String, Object> message = new HashMap<>();
                message.put("hostId", "未能通过 " + hostId + " 找到对应的 List HDomain 数据信息！");
                return RestResult.error(message);
            }
            List<Map<String, Object>> icpInfos = domainServiceImpl.icpInfos(bindDomain);
            return RestResult.ok(icpInfos);
        }

        return RestResult.error();
    }

    /**
     * 通过参数 domainName 以及 hostId 对域名 icp 信息重新获取，同时写入到数据库、Redis缓存中
     * <p>
     * 只对用户绑定的域名生效，不会对系统域名生效
     *
     * @param domainName
     * @param hostId
     * @return
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws JsonProcessingException
     * @throws IOException
     */
    @PostMapping("/reseticp")
    @ResponseBody
    public RestResult reseticp(
            // 重置缓存的域名信息
            @RequestParam(value = "domainName", required = false, defaultValue = "") String domainName,
            // 重置缓存的 hostId 信息
            @RequestParam(value = "hostId", required = false, defaultValue = "-1") long hostId)
            throws IOException {

        if (StrUtil.isBlank(domainName) && hostId < 1) {
            Map<String, Object> message = new HashMap<>();
            message.put("domainName,hostId", "domainName 参数为空同时参数 hostId 小于1！");
            return RestResult.error(message);
        }

        if (StrUtil.isNotBlank(domainName)) {
            HDomain hDomain = domainServiceImpl.getInfoByDomainName(domainName);
            if (hDomain == null) {
                Map<String, Object> message = new HashMap<>();
                message.put("domainName", "未能通过 " + domainName + " 找到对应的 HDomain 数据信息！");
                return RestResult.error(message);
            }
            domainServiceImpl.resetIcp(hDomain);
        }

        if (hostId > 0) {
            Host host = hostServiceImpl.getInfoById(hostId);
            if (host == null) {
                Map<String, Object> message = new HashMap<>();
                message.put("hostId", "未能通过 " + hostId + " 找到对应的 Host 数据信息！");
                return RestResult.error(message);
            }
            List<HDomain> bindDomain = domainServiceImpl.bindIndos(hostId);
            if (CollectionUtils.isEmpty(bindDomain)) {
                Map<String, Object> message = new HashMap<>();
                message.put("hostId", "未能通过 " + hostId + " 找到对应的 List HDomain 数据信息！");
                return RestResult.error(message);
            }
            domainServiceImpl.resetIcp(bindDomain);
        }

        return RestResult.ok(1);
    }

    /**
     * 获取 绑定域名的 nginx server 的配置信息
     *
     * @return
     * @throws IOException
     * @throws JsonProcessingException
     * @throws JsonMappingException
     * @throws JsonParseException
     */
    @PostMapping("/bindomaininfo")
    @ResponseBody
    public RestResult bindomaininfo(
            // 重置缓存的域名信息
            @RequestParam(value = "domainName", required = false, defaultValue = "") String domainName,
            // 重置缓存的 hostId 信息
            @RequestParam(value = "hostId", required = false, defaultValue = "-1") long hostId)
            throws IOException {
        if (StrUtil.isBlank(domainName) && hostId < 1) {
            Map<String, Object> message = new HashMap<>();
            message.put("domainName,hostId", "domainName 参数为空同时参数 hostId 小于1！");
            return RestResult.error(message);
        }

        List<NodeDomainNgxinServer> result = new ArrayList<>();
        if (StrUtil.isNotBlank(domainName)) {
            String asciiDomain = DomainUtil.toASCII(domainName);
            if (StrUtil.isBlank(asciiDomain)) {
                return RestResult.error(domainName + " 非有效域名。");
            }
            HDomain hDomain = domainServiceImpl.getInfoByDomainName(domainName);
            if (hDomain == null) {
                Map<String, Object> message = new HashMap<>();
                message.put("domainName", "未能通过 " + domainName + " 找到对应的 HDomain 数据信息！");
                return RestResult.error(message);
            }
            // 获取域名的 nginx server 配置信息
            String ngxinServerContent = domainServiceImpl.ngxinServerContent(hDomain);
            Date nginxServerFileLastModified = domainServiceImpl.nginxServerFileLastModified(hDomain);

            NodeDomainNgxinServer nodeDomainNgxinServer = new NodeDomainNgxinServer();
            nodeDomainNgxinServer.setDomain(hDomain.getDomainName());
            nodeDomainNgxinServer.setNgxinServer(ngxinServerContent);
            nodeDomainNgxinServer.setFileLastModified(nginxServerFileLastModified);

            result.add(nodeDomainNgxinServer);
        }

        if (hostId > 0) {
            Host host = hostServiceImpl.getInfoById(hostId);
            if (host == null) {
                Map<String, Object> message = new HashMap<>();
                message.put("hostId", "未能通过 " + hostId + " 找到对应的 Host 数据信息！");
                return RestResult.error(message);
            }
            List<HDomain> bindDomain = domainServiceImpl.bindIndos(hostId);
            if (CollectionUtils.isEmpty(bindDomain)) {
                Map<String, Object> message = new HashMap<>();
                message.put("hostId", "未能通过 " + hostId + " 找到对应的 List HDomain 数据信息！");
                return RestResult.error(message);
            }
            for (HDomain hDomain : bindDomain) {
                NodeDomainNgxinServer nodeDomainNgxinServer = new NodeDomainNgxinServer();
                nodeDomainNgxinServer.setDomain(hDomain.getDomainName());
                nodeDomainNgxinServer.setNgxinServer(domainServiceImpl.ngxinServerContent(hDomain));
                nodeDomainNgxinServer.setFileLastModified(domainServiceImpl.nginxServerFileLastModified(hDomain));
                result.add(nodeDomainNgxinServer);
            }
        }

        return RestResult.ok(result);
    }

    /**
     * 重新绑定域名的 nginx server 的信息
     *
     * @return
     * @apiNote 如果 all = 1，则后续的 domainName、hostId 参数失去作用，整个节点的 domain 重新生成 nginx
     * server 的配置文件并且重启nginx
     */
    @PostMapping("/rebinddomain")
    @ResponseBody
    public RestResult rebindomain(
            // 重置缓存的域名信息
            @RequestParam("all") int all,
            // 重置缓存的域名信息
            @RequestParam(value = "domainName", required = false, defaultValue = "") String domainName,
            // 重置缓存的 hostId 信息
            @RequestParam(value = "hostId", required = false, defaultValue = "-1") long hostId)
            throws IOException {
        log.info("rebindomain: " + MapUtil.join(request.getParameterMap(), ";", ":", ""));
        if (all == 1) {
            // 通过异步的方式实现
            asyncDomainServiceImpl.asyncBindDomain();
            return RestResult.ok(1);
        }

        if (StrUtil.isBlank(domainName) && hostId < 1) {
            Map<String, Object> message = new HashMap<>();
            message.put("domainName,hostId", "domainName 参数为空同时参数 hostId 小于1！");
            return RestResult.error(message);
        }

        if (StrUtil.isNotBlank(domainName)) {
            HDomain hDomain = domainServiceImpl.getInfoByDomainName(domainName);
            if (hDomain == null) {
                Map<String, Object> message = new HashMap<>();
                message.put("domainName", "未能通过 " + domainName + " 找到对应的 HDomain 数据信息！");
                return RestResult.error(message);
            }
            // 异步配置域名的 nginx server 信息
            asyncDomainServiceImpl.bindDomain(hDomain);
        }

        if (hostId > 0) {
            Host host = hostServiceImpl.getInfoById(hostId);
            if (host == null) {
                Map<String, Object> message = new HashMap<>();
                message.put("hostId", "未能通过 " + hostId + " 找到对应的 Host 数据信息！");
                return RestResult.error(message);
            }
            List<HDomain> bindDomain = domainServiceImpl.bindIndos(hostId);
            if (CollectionUtils.isEmpty(bindDomain)) {
                return RestResult.ok(1);
            }

            // 异步配置域名的 nginx server 信息
            asyncDomainServiceImpl.bindDomain(hostId);

        }

        return RestResult.ok(1);
    }

    /**
     * 通过参数 hostId 获取到系统默认域名，然后修改 accessType 数据字段
     *
     * @param hostId
     * @param accessType
     * @return
     * @throws JsonParseException
     * @throws JsonMappingException
     * @throws JsonProcessingException
     * @throws IOException
     */
    @PostMapping("/updateaccesstype")
    @ResponseBody
    public RestResult updateaccesstype(
            // hostId 的查询过滤参数
            @RequestParam("hostId") long hostId,
            // 修改 accessType 的值
            @RequestParam("accessType") short accessType) throws IOException {

        Host host = hostServiceImpl.getInfoById(hostId);
        if (host == null) {
            Map<String, Object> message = new HashMap<>();
            message.put("hostId", "未能通过 " + hostId + " 找到对应的 Host 数据信息！");
            return RestResult.error(message);
        }

        HDomain hDomain = domainServiceImpl.getDefaultDomain(hostId);
        hDomain.setAccessType(accessType);

        boolean updateItem = domainServiceImpl.updateItem(hDomain);
        if (!updateItem) {
            Map<String, Object> message = new HashMap<>();
            message.put("accessType", "修改 accessType 失败！");
            return RestResult.error(message);
        }

        return RestResult.ok(1);
    }
}
